﻿#include "pch.h"
#include "sbcs.h"
#include "extend/sbvalues.h"
#if _SB_WINDOWS_
#if _SB_WINDOWS_DESKTOP_
#define INCL_WINSOCK_API_TYPEDEFS   1
#endif
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#if !_SB_WINDOWS_PHONE_
#include <mswsock.h>
#endif
#if !_SB_WINDOWS_DESKTOP_
#pragma comment(lib, "ws2_32")
#endif
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#endif

#ifndef INVALID_SOCKET
// changed to intptr (LINUX Kernel 3, FreeBSD 10)
#define INVALID_SOCKET  (kintptr)(~0)
#endif

#ifndef SOCKET_ERROR
#define SOCKET_ERROR    -1
#endif

#if _SB_WINDOWS_
#if _SB_WINDOWS_DESKTOP_
#undef FD_ISSET
#define FD_ISSET(fd, set) KSKFN(__WSAFDIsSet)((SOCKET)(fd), (fd_set FAR *)(set))
typedef int(PASCAL FAR* LPFN___WSAFDIsSet)(SOCKET fd, fd_set FAR*);
#endif
#define SHUT_RDWR       SD_BOTH
#define SHUT_RD         SD_RECEIVE
#define SHUT_WR         SD_SEND
#define socklen_t       int
#else
#define SOCKET          kintptr
#define closesocket     close
#endif


//////////////////////////////////////////////////////////////////////////
// 소켓
static struct kImplSk
{
	kcham				isinit;

#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
	HMODULE					dll_ws2;
	LPFN_WSACLEANUP			func_WSACleanup;
	LPFN_WSASTARTUP			func_WSAStartup;
	LPFN_WSAGETLASTERROR	func_WSAGetLastError;
	LPFN_INET_ADDR			func_inet_addr;
	LPFN_GETHOSTNAME		func_gethostname;
	LPFN_GETHOSTBYNAME		func_gethostbyname;
	LPFN_INET_NTOA			func_inet_ntoa;
	LPFN_CLOSESOCKET		func_closesocket;
	LPFN_WSASOCKETW			func_WSASocketW;
	LPFN_WSACONNECT			func_WSAConnect;
	LPFN_ACCEPT				func_accept;
	LPFN_SHUTDOWN			func_shutdown;
	LPFN_BIND				func_bind;
	LPFN_LISTEN				func_listen;
	LPFN_WSAPOLL			func_WSAPoll;
	LPFN_SELECT				func_select;
	LPFN_IOCTLSOCKET		func_ioctlsocket;
	LPFN_GETSOCKNAME		func_getsockname;
	LPFN_GETPEERNAME		func_getpeername;
	LPFN_RECV				func_recv;
	LPFN_SEND				func_send;
	LPFN_RECVFROM			func_recvfrom;
	LPFN_SENDTO				func_sendto;
	LPFN_GETPROTOBYNAME		func_getprotobyname;
	LPFN_GETSOCKOPT			func_getsockopt;
	LPFN_SETSOCKOPT			func_setsockopt;
	LPFN___WSAFDIsSet		func___WSAFDIsSet;
#ifndef SB_NO_IPV6
	LPFN_INET_NTOPA			func_inet_ntop;
#endif
	LPFN_GETADDRINFO		func_getaddrinfo;
	LPFN_FREEADDRINFO		func_freeaddrinfo;
#else
	int					thd;
#endif

#ifndef SOL_IP
	kcham				sol_ip_cache;
	int					sol_ip_proto;
#endif

#ifndef SOL_IPV6
	kcham				sol_ipv6_cache;
	int					sol_ipv6_proto;
#endif

#ifndef SOL_TCP
	kcham				sol_tcp_cache;
	int					sol_tcp_proto;
#endif

#ifndef SOL_UDP
	kcham				sol_udp_cache;
	int					sol_udp_proto;
#endif

	kcham				trace_errno;
} k_impl_sk =
{
	FALSE,

#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
#ifndef SB_NO_IPV6
	NULL,
#endif
	NULL,
	NULL,
#else
	0,
#endif

#ifndef SOL_IP
	FALSE,
	0,
#endif

#ifndef SOL_IPV6
	FALSE,
	0,
#endif

#ifndef SOL_TCP
	FALSE,
	0,
#endif

#ifndef SOL_UDP
	FALSE,
	0,
#endif

	FALSE,
};

#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
#define KSKFN(x)       K_CONCAT(k_impl_sk.func_,x)
#else
#define KSKFN(x)       x
#endif

// 제거
static void _k_disp_socket(kpointer ptr)
{
	if (!k_impl_sk.isinit)
		return;

	k_impl_sk.isinit = FALSE;

#if _SB_WINDOWS_
	KSKFN(WSACleanup)();

#if _SB_WINDOWS_DESKTOP_
	FreeLibrary(k_impl_sk.dll_ws2);
#endif
#endif
}

// 초기화
static kcham _k_init_socket(void)
{
	if (k_impl_sk.isinit)
		return TRUE;

#if _SB_WINDOWS_
#if _SB_WINDOWS_DESKTOP_
#define KSKDEF(x,l)    K_STMT_BEGIN{KSKFN(x)=(K_CONCAT(LPFN_,l))GetProcAddress(k_impl_sk.dll_ws2, K_STRING(x)); if (!KSKFN(x)) return FALSE;}K_STMT_END
#define KSKIFF(x,l)    K_STMT_BEGIN{KSKFN(x)=(K_CONCAT(LPFN_,l))GetProcAddress(k_impl_sk.dll_ws2, K_STRING(x));}K_STMT_END

	if (!k_impl_sk.dll_ws2)
	{
		k_impl_sk.dll_ws2 = LoadLibrary(L"ws2_32");

		if (!k_impl_sk.dll_ws2)
			return FALSE;
	}

	KSKDEF(WSAStartup, WSASTARTUP);
	KSKDEF(WSACleanup, WSACLEANUP);
	KSKDEF(WSAGetLastError, WSAGETLASTERROR);
	KSKDEF(inet_addr, INET_ADDR);
	KSKDEF(gethostname, GETHOSTNAME);
	KSKDEF(gethostbyname, GETHOSTBYNAME);
	KSKDEF(inet_ntoa, INET_NTOA);
	KSKDEF(closesocket, CLOSESOCKET);
	KSKDEF(WSASocketW, WSASOCKETW);
	KSKDEF(WSAConnect, WSACONNECT);
	KSKDEF(accept, ACCEPT);
	KSKDEF(shutdown, SHUTDOWN);
	KSKDEF(bind, BIND);
	KSKDEF(listen, LISTEN);
	KSKDEF(select, SELECT);
	KSKDEF(ioctlsocket, IOCTLSOCKET);
	KSKDEF(getsockname, GETSOCKNAME);
	KSKDEF(getpeername, GETPEERNAME);
	KSKDEF(recv, RECV);
	KSKDEF(send, SEND);
	KSKDEF(recvfrom, RECVFROM);
	KSKDEF(sendto, SENDTO);
	KSKDEF(getprotobyname, GETPROTOBYNAME);
	KSKDEF(getsockopt, GETSOCKOPT);
	KSKDEF(setsockopt, SETSOCKOPT);
	KSKDEF(__WSAFDIsSet, __WSAFDIsSet);
	KSKIFF(WSAPoll, WSAPOLL);
#ifndef SB_NO_IPV6
	KSKIFF(inet_ntop, INET_NTOPA);
#endif
	KSKIFF(getaddrinfo, GETADDRINFO);
	KSKIFF(freeaddrinfo, FREEADDRINFO);

#undef KSKDEF
#undef KSKIFF
#endif
	K_STMT_BEGIN{
		WSADATA wsa;
		KSKFN(WSAStartup)(MAKEWORD(2, 2), &wsa);
	} K_STMT_END;
#endif

	k_impl_sk.isinit = TRUE;

	k_atexit_p(_k_disp_socket, NULL);

	return TRUE;
}

// 패밀리에서 크기 얻기

// 소켓주소(sockaddr) 크기
static int _k_sock_get_family_size(kint family)
{
	switch (family)
	{
		case KNAF_UNIX:
		case KNAF_INET:
			return sizeof(struct sockaddr_in);

		case KNAF_INET6:
			return sizeof(struct sockaddr_in6);
	}

	return -1;
}

// 오류 처리
static void _k_sock_errno(void)
{
	if (k_impl_sk.trace_errno)
	{
		int err;

#if _SB_WINDOWS_
		err = KSKFN(WSAGetLastError)();
#else
		err = errno;
#endif

		char sz[K_MAX_PATH];
		k_syserr(sz, K_MAX_PATH - 1, err);
		k_tracef(KLV_ERROR, "k.net", "[%d] %s", err, sz);
	}
}

// 오류 보이기
void k_net_trace_errno(kcham isenable)
{
	k_impl_sk.trace_errno = isenable;
}


//////////////////////////////////////////////////////////////////////////
// 주소 정보

/**
 * 주소 정보를 질의한다.
 * 반환 값은 nhis 인수에 들어 가며 사용한 다음에는 k_free 함수로 제거해야 한다.
 * @param	nodename		노드 이름.
 * @param	servname		서비스 이름.
 * @param	hint			힌트.
 * @param [반환]	nhis	(널값이 아님) 주소 정보가 들어갈 내용.
 * @return	주소 정보 개수.
 */
kint k_nai_query(const char* nodename, const char* servname, const kNai* hint, kNai** nhis)
{
	int ret;
	struct addrinfo oh;
	struct addrinfo* res;
	struct addrinfo* ptr;
	kNai* tmp;

	k_return_value_if_fail(nhis, -1);

	if (!_k_init_socket())
		return -1;

#if _SB_WINDOWS_DESKTOP_
	if (!KSKFN(getaddrinfo))
	{
		// 함수가 없어서 못함
		return -1;
	}
#endif

	memset(&oh, 0, sizeof(struct addrinfo));

	if (hint)
	{
		oh.ai_flags = hint->flags;
		oh.ai_family = hint->family;
		oh.ai_socktype = hint->socktype;
		oh.ai_protocol = hint->protocol;
	}
	else
	{
		oh.ai_family = AF_UNSPEC;
		oh.ai_socktype = SOCK_STREAM;
		oh.ai_protocol = IPPROTO_TCP;
	}

	ret = KSKFN(getaddrinfo)(nodename, servname, &oh, &res);

	if (ret != 0)
	{
		_k_sock_errno();
		return -1;
	}

	for (ret = 0, ptr = res; ptr != NULL; ptr = ptr->ai_next)
		ret++;

	if (ret == 0)
		*nhis = NULL;
	else
	{
		tmp = k_new_zero(ret, kNai);
		*nhis = tmp;

		for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
		{
			tmp->flags = ptr->ai_flags;
			tmp->family = ptr->ai_family;
			tmp->socktype = ptr->ai_socktype;
			tmp->protocol = ptr->ai_protocol;
			if (ptr->ai_addrlen >= sizeof(kNai))
				tmp->len = 0;
			else
			{
				tmp->len = ptr->ai_addrlen;
				memcpy(&tmp->nar, ptr->ai_addr, ptr->ai_addrlen);
			}
			tmp->cname = ptr->ai_canonname;

			tmp++;
		}
	}

	KSKFN(freeaddrinfo)(res);

	return ret;
}

/**
 * 주소 정보를 문자열로 바꿈.
 * @param [입력]	self	  	개체나 인터페이스의 자기 자신 값.
 * @param [반환]	buffer	(널값이 아님) 버퍼 문자열.
 * @param	size		  	버퍼 크기.
 * @return	버퍼 문자열의 길이.
 */
kint k_nai_tos(const kNai* self, char* buffer, kint size)
{
	const char* af;
	const char* st;
	const char* prot;
	char b_af[K_MAX_PATH];
	char b_st[K_MAX_PATH];
	char b_prot[K_MAX_PATH];

	switch (self->family)
	{
		case KNAF_UNSPEC:
			af = "Unspec";
			break;

		case KNAF_UNIX:
			af = "Unix";
			break;

		case KNAF_INET:
			af = b_af;
			k_nar_resolve_addr(&self->nar, b_prot, K_MAX_PATH - 1);
			k_snprintf(b_af, K_MAX_PATH - 1, "INET(IPv4 '%s')", b_prot);
			break;

		case KNAF_INET6:
			af = b_af;
			k_nar_resolve_addr(&self->nar, b_prot, K_MAX_PATH - 1);
			k_snprintf(b_af, K_MAX_PATH - 1, "INET6(IPv6 '%s')", b_prot);
			break;

#if _SB_WINDOWS_
		case AF_NETBIOS:
			af = "NetBIOS";
			break;
#endif

		default:
			af = b_af;
			k_snprintf(b_af, K_MAX_PATH - 1, "AF[%d]", self->family);
			break;
	}

	switch (self->socktype)
	{
		case 0:
			st = "Unspec";
			break;

		case KNST_STREAM:
			st = "Stream";
			break;

		case KNST_DATAGRAM:
			st = "DataGram";
			break;

		case KNST_RAW:
			st = "Raw";
			break;

		case KNST_RDM:
			st = "RDM";
			break;

		case KNST_SEQPACKET:
			st = "SEQPACKET";
			break;

		default:
			st = b_st;
			k_snprintf(b_st, K_MAX_PATH - 1, "ST[%d]", self->socktype);
			break;
	}

	switch (self->protocol)
	{
		case KNPROT_IP:
			prot = "IP(Unspec)";
			break;

		case KNPROT_ICMP:
			prot = "ICMP";
			break;

		case KNPROT_IGMP:
			prot = "IGMP";
			break;

		case KNPROT_GGP:
			prot = "GGP";
			break;

		case KNPROT_IPV4:
			prot = "IPV4";
			break;

		case KNPROT_TCP:
			prot = "TCP";
			break;

		case KNPROT_UDP:
			prot = "UDP";
			break;

		case KNPROT_IPV6:
			prot = "IPV6";
			break;

		case KNPROT_ROUTING:
			prot = "ROUTING";
			break;

		case KNPROT_FRAGMENT:
			prot = "FRAGMENT";
			break;

		case KNPROT_ESP:
			prot = "ESP";
			break;

		case KNPROT_AH:
			prot = "AH";
			break;

		case KNPROT_ICMPV6:
			prot = "ICMPV6";
			break;

		case KNPROT_NONE:
			prot = "NONE";
			break;

		case KNPROT_DSTOPTS:
			prot = "DSTOPTS";
			break;

		case KNPROT_ND:
			prot = "ND";
			break;

		case KNPROT_RAW:
			prot = "RAW";
			break;

		default:
			prot = b_prot;
			k_snprintf(b_prot, K_MAX_PATH - 1, "PROT[%d]", self->protocol);
			break;
	}

	return k_snprintf(buffer, size, "[%08X] %s, %s, %s", self->family, af, st, prot);
}


//////////////////////////////////////////////////////////////////////////
// 주소

/**
 * 네트워크 주소 얻기.
 * @param [반환]	buf	(널값이 아님) 주소 정보의 버퍼.
 * @param	size	   	버퍼 크기.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_look_host_name(char* buf, kint size)
{
	if (!_k_init_socket())
		return FALSE;

	return KSKFN(gethostname)(buf, size) == 0;
}

/**
 * 주소 정보 테이블 초기화.
 * @param [입력]	self   	개체나 인터페이스의 자기 자신 값.
 * @param	count	   	IPV4는 4를, IPV6는 16을 지정한다
 * @param [입력]	주소 정보가 들어 있는 테이블 배열
 * @param	port	   	포트 번호.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init_table(kNar* self, kint count, kbyte* tbl, kushort port)
{
	k_return_value_if_fail(tbl != NULL, FALSE);

	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));

	if (count == 4)
	{
		kuint addr = ((tbl[3] << 24 | tbl[2] << 16 | tbl[1] << 8 | tbl[0]) & 0xFFFFFFFF);
		self->v4.addr = k_htonl(addr);
		self->family = KNAF_INET;
	}
#ifndef SB_NO_IPV6
	else if (count == 16)
	{
		kuint i;

		for (i = 0; i < 8; i++)
			self->v6.addr[i] = (kushort)(tbl[i * 2] * 256 + tbl[i * 2 + 1]);

		self->family = KNAF_INET6;
	}
#endif
	else
	{
		// 뭘까
		return FALSE;
	}

	self->port = k_htons(port);

	return TRUE;
}

/**
 * 주소를 IPV4로 초기화 한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	addr		주소.
 * @param	port		포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init4(kNar* self, kuint addr, kushort port)
{
	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));
	self->v4.addr = addr;
	self->port = k_htons(port);
	self->family = KNAF_INET;

	return TRUE;
}

/**
 * 주소를 IPV4로 초기화 하는데, DNS나 WINS로 주소 값을 찾는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	addr		주소.
 * @param	port		포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init4_resolve(kNar* self, const char* addr, kushort port)
{
	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));

	// 얘네 나중에 바꾸어야함 이 함수 때문에 _WINSOCK_DEPRECATED_NO_WARNINGS를 적용
	self->v4.addr = (addr != NULL && *addr != '\0') ? KSKFN(inet_addr)(addr) : INADDR_ANY;

	if (self->v4.addr == INADDR_NONE)
	{
		struct hostent* phe = KSKFN(gethostbyname)(addr);

		if (!phe)
		{
			_k_sock_errno();
			return FALSE;
		}

		self->v4.addr = ((struct in_addr*)phe->h_addr)->s_addr;
	}

	self->port = k_htons(port);
	self->family = KNAF_INET;

	return TRUE;
}

/**
 * IPV4를 초기화 하는데 특수 주소를 사용한다.
 * where 인수에 따라 0=NONE, 1=ANY, 2=LOOPBACK, 3=BROADCAST 의 의미를 갖는다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	where   	특수 주소 순번.
 * @param	port		포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init4_specify(kNar* self, kint where, kushort port)
{
	static const kint s_desc[] =
	{
		INADDR_NONE,
		INADDR_ANY,
		INADDR_LOOPBACK,
		INADDR_BROADCAST,
	};

	if (where < KNSW_NONE || where > KNSW_BROADCAST)
		return FALSE;

	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));

	self->v4.addr = s_desc[where];
	self->port = k_htons(port);
	self->family = KNAF_INET;

	return TRUE;
}

/**
 * 주소를 IPV6 값으로 초기화 한다.
 * @param [입력]	self		개체나 인터페이스의 자기 자신 값.
 * @param [입력]	addr	(널값이 아님) 주소.
 * @param	port			포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init6(kNar* self, kushort* addr, kushort port)
{
	k_return_value_if_fail(addr != NULL, FALSE);

	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));
	memcpy(self->v6.addr, addr, sizeof(kushort) * 8);
	self->port = k_htons(port);
	self->family = KNAF_INET6;

	return TRUE;
}

/**
 * 주소를 IPV6 값으로 초기화한다.
 * @param [입력]	self		개체나 인터페이스의 자기 자신 값.
 * @param [입력]	addr	(널값이 아님) 주소.
 * @param	port			포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init6_byte(kNar* self, kbyte* addr, kushort port)
{
	k_return_value_if_fail(addr != NULL, FALSE);

	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));
	memcpy(self->v6.vaddr, addr, sizeof(kbyte) * 16);
	self->port = k_htons(port);
	self->family = KNAF_INET6;

	return TRUE;
}

/**
 * 주소를 IPV6 값으로 초기화하는데, 특수 주소를 사용한다.
 * where 인수에 따라 0=ANY, 1=ANY, 2=LOOPBACK, 3=MULTICAST의 의미를 갖는다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	where   	특수 주소 순번.
 * @param	port		포트.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_nar_init6_specify(kNar* self, kint where, kushort port)
{
#if _SB_WINDOWS_
	static const kbyte s_desc[][16] =
	{
		IN6ADDR_ANY_INIT,
		IN6ADDR_ANY_INIT,
		IN6ADDR_LOOPBACK_INIT,
		IN6ADDR_MULTICASTPREFIX_INIT,
	};
#endif

	if (where < KNSW_NONE || where > KNSW_BROADCAST)
		return FALSE;

	if (!_k_init_socket())
		return FALSE;

	memset(self, 0, sizeof(kNar));

#if _SB_WINDOWS_
	memcpy(self->v6.addr, s_desc[where], sizeof(kbyte) * 16);
#else
	switch (where)
	{
		case KNSW_NONE:
		case KNSW_ANY:
			memcpy(self->v6.addr, &in6addr_any, sizeof(kushort)*8);
			break;

		case KNSW_LOOPBACK:
			memcpy(self->v6.addr, &in6addr_loopback, sizeof(kushort)*8);
			break;

		case KNSW_BROADCAST:
			//  LINUX, BSD에 없더라... loopback으로...
			//memcpy(self->v6.addr, &in6addr_multicastprefix, sizeof(kushort)*8);
			memcpy(self->v6.addr, &in6addr_loopback, sizeof(kushort)*8);
			break;

		default:
			return FALSE;
	}
#endif

	self->port = k_htons(port);
	self->family = KNAF_INET6;

	return TRUE;
}

/**
 * 주소에서 IPV4 값을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	IPV4 주소.
 */
kuint k_nar_get_addr4(const kNar* self)
{
	return self->family == KNAF_INET ? k_ntohl(self->v4.addr) : INADDR_NONE;
}

/**
 * 주소의 IPV를 숫자와 점으로 구성된 문자열로 얻는다.
 * @param [입력]	self	  	개체나 인터페이스의 자기 자신 값.
 * @param [입력,반환]	buffer	(널값이 아님) 문자열 버퍼.
 * @param	size		  	버퍼의 크기.
 * @return	문자열 버퍼의 길이.
 */
int k_nar_numeric_addr(const kNar* self, char* buffer, kint size)
{
	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(size > 0, -1);

	if (self->family == KNAF_INET)
	{
		return k_snprintf(buffer, size, "%d.%d.%d.%d:%d",
			self->v4.vaddr.b.a, self->v4.vaddr.b.b, self->v4.vaddr.b.c, self->v4.vaddr.b.d,
			k_ntohs(self->port));
	}
	else
	{
		return k_snprintf(buffer, size, "%x:%x:%x:%x:%x:%x:%x:%x.%d",
			self->v6.addr[0], self->v6.addr[1], self->v6.addr[2], self->v6.addr[3],
			self->v6.addr[4], self->v6.addr[5], self->v6.addr[6], self->v6.addr[7],
			k_ntohs(self->port));
	}
}

/**
 * 주소의 IPV를 주소로 조회한다.
 * @param [입력]	self	  	개체나 인터페이스의 자기 자신 값.
 * @param [입력,반환]	buffer	(널값이 아니면) 문자열 버퍼.
 * @param	size		  	버퍼의 크기.
 * @return	문자열 버퍼의 길이.
 */
kint k_nar_resolve_addr(const kNar* self, char* buffer, kint size)
{
	// 전체를 inet_ntop으로 하는게 더 낫다...
	// 그런데 왜 이렇게 했을까...

	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(size > 0, -1);

	*buffer = '\0';

	if (!_k_init_socket())
		return -1;

	if (self->family == KNAF_INET)
	{
		char* tmp = KSKFN(inet_ntoa)(*(struct in_addr*)&self->v4);

		if (!tmp)
		{
			_k_sock_errno();
			return -1;
		}

		k_strncpy(buffer, tmp, size);

		return (kint)k_strlen(buffer);
	}

#ifndef SB_NO_IPV6
	else if (self->family == KNAF_INET6)
	{
#if _SB_WINDOWS_DESKTOP_ || _SB_WINDOWS_STORE_
		const char* tmp;

#if _SB_WINDOWS_DESKTOP_
		if (!KSKFN(inet_ntop))
		{
			// 이 함수는 윈도우 비스타 이상만 됨
			// 윈도우 2000도 지원 하려면 'WSAAddressToString'함수로 바꾸면 되긴 함.
			// 왜 안바꿨을까용...
			return -1;
		}
#endif

		// inet_ntop <- 이거 윈폰8에서 되는지 알 수 없음, 스토어 8.1은 됨
		tmp = KSKFN(inet_ntop)(KNAF_INET6, (kpointer)&self->v6, buffer, size);

		if (!tmp)
		{
			_k_sock_errno();
			return -1;
		}

		return (kint)k_strlen(buffer);
#else
		// 윈도우폰은 inet_ntop이 없음. 아마 IPV6가 안되나 봄
		// WSAAddressToStringA() 함수가 되긴하는데 어케 쓰는지 모르게씀
		return -1;
#endif
	}
#endif

	return -1;
}

/**
 * 주소 정보에서 포트를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	포트.
 */
kushort k_nar_get_port(const kNar* self)
{
	return k_ntohs(self->port);
}

/**
 * 주소 정보에서 빠밀리를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	패밀리.
 */
kint k_nar_get_family(const kNar* self)
{
	return self->family;
}

/**
 * 주소 정보에 포트를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	port		포트.
 */
void k_nar_set_port(kNar* self, kushort port)
{
	self->port = k_htons(port);
}

/**
 * 주소 정보가 올바른지 확인한다. IPV4 또는 IPV6이어야 한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	검사에 성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_nar_test(const kNar* self)
{
	return self->family == KNAF_INET || self->family == KNAF_INET6;
}

/**
 * 주소 정보를 비교한다
 * @param	left 	왼쪽 주소.
 * @param	right	오른쪽 주소.
 * @return	개체가 같다고 판명되면 참를, 그렇지 않으면 거짓를 반환한다.
 */
kcham k_nar_eq(const kNar* left, const kNar* right)
{
	if (left->family != right->family)
		return FALSE;

	if (left->family == KNAF_INET)
		return left->v4.addr == right->v4.addr;

#ifndef SB_NO_IPV6
	if (left->family == KNAF_INET6)
		return memcmp(left->v6.addr, right->v6.addr, sizeof(kushort) * 8) == 0;
#endif

	return FALSE;
}

/**
 * 소켓주소(sockaddr) 크기.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	sockaddr 형식의 크기.
 */
int k_nar_get_size(const kNar* self)
{
	return _k_sock_get_family_size(self->family);
}


//////////////////////////////////////////////////////////////////////////
// 소켓
struct kSocket
{
	SOCKET				fd;

	kcham				isconn : 8;
	kcham				isblock : 8;
	kcham				islisten : 8;
	kint				mode : 8;

	kint				af : 16;
	kint				st : 16;
	kint				pt : 16;
	kint				async : 16;

	kint				selmode;

	kNar				nar_local;
	kNar				nar_bind;
	kNar				nar_rmt;

	kpointer			userdata;
};

/**
 * 소켓을 만들고 초기화한다.
 * @param	family  	패밀리.
 * @param	socktype	소켓 형식.
 * @param	protocol	프로토콜.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kSocket*.
 */
kSocket* k_sock_new(int family, int socktype, int protocol)
{
	kSocket* self;
	SOCKET fd;

	if (!_k_init_socket())
		return NULL;

#if _SB_WINDOWS_
	fd = KSKFN(WSASocketW)(family, socktype, protocol, NULL, 0, WSA_FLAG_OVERLAPPED);

	if (fd == INVALID_SOCKET)
	{
		_k_sock_errno();
		return NULL;
	}
#else
	fd = KSKFN(socket)(family, socktype, protocol);

	if (fd < 0)
	{
		_k_sock_errno();
		return NULL;
	}

	if (KSKFN(fcntl)(fd, F_SETFD, 1) < 0)
	{
		_k_sock_errno();
		KSKFN(close)(fd);
		return NULL;
	}

	int one = -1;

	if (KSKFN(setsockopt)(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int)) < 0)
	{
		_k_sock_errno();
		KSKFN(close)(fd);
		return NULL;
	}
#endif

	self = k_new_0(kSocket);
	self->fd = fd;
	self->af = family;
	self->st = socktype;
	self->pt = protocol;
	self->isblock = TRUE;

	return self;
}

/**
 * 주소 정보로 소켓을 만든다.
 * @param	nai	주소 정보.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kSocket*.
 */
kSocket* k_sock_new_ai(const kNai* nai)
{
	k_return_value_if_fail(nai, NULL);

	return k_sock_new(nai->family, nai->socktype, nai->protocol);
}

// 내부 소켓
static kSocket* k_sock_new_handle(kintptr handle, int family, int socktype, int protocol)
{
	kSocket* self = k_new_0(kSocket);

	self->fd = (SOCKET)handle;
	self->af = family;
	self->st = socktype;
	self->pt = protocol;
	self->isblock = TRUE;

	return self;
}

/**
 * 소켓을 제거한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 */
void k_sock_delete(kSocket* self)
{
	if (self->fd != INVALID_SOCKET)
	{
		if (KSKFN(closesocket)(self->fd) != 0)
			_k_sock_errno();
	}

	k_delete(self);
}

/**
 * 소켓을 닫는다. 연결되어 있는 소켓만 닫으며 소켓 자체를 제거하지는 않는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참, 실패하면 거짓를 반환한다.
 */
kcham k_sock_close(kSocket* self)
{
	kcham ret;

	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	if (KSKFN(closesocket)(self->fd) == 0)
		ret = TRUE;
	else
	{
		_k_sock_errno();
		ret = FALSE;
	}

	self->fd = INVALID_SOCKET;

	self->isconn = FALSE;
	self->isblock = FALSE;
	self->islisten = FALSE;
	self->mode = 0;

	self->async = 0;

	self->selmode = 0;

	self->nar_local.family = 0;
	self->nar_bind.family = 0;
	self->nar_rmt.family = 0;

	return ret;
}

/**
 * 소켓을 재사용할 수 있게 한다. 만들어서 사용하던 소켓을 다시 쓰려면 이 기능을 이용한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_reuse(kSocket* self)
{
	SOCKET fd;

	k_return_value_if_fail(self->fd == INVALID_SOCKET, FALSE);

#if _SB_WINDOWS_
	fd = KSKFN(WSASocketW)(self->af, self->st, self->pt, NULL, 0, WSA_FLAG_OVERLAPPED);

	if (fd == INVALID_SOCKET)
	{
		_k_sock_errno();
		return FALSE;
	}

#else
	fd = KSKFN(socket)(self->af, self->st, self->pt);

	if (fd < 0)
	{
		_k_sock_errno();
		return FALSE;
	}

	if (KSKFN(fcntl)(fd, F_SETFD, 1) < 0)
	{
		_k_sock_errno();
		KSKFN(close)(fd);
		return FALSE;
	}

	int one = -1;

	if (KSKFN(setsockopt)(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int)) < 0)
	{
		_k_sock_errno();
		KSKFN(close)(fd);
		return FALSE;
	}
#endif

	self->fd = fd;
	self->isblock = TRUE;

	return TRUE;
}

/**
 * 소켓을 셧다운한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	isread  	읽기 기능을 쓰려면 참으로 넣는다.
 * @param	iswrite 	쓰기 기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_shutdown(kSocket* self, kcham isread, kcham iswrite)
{
	int n;

	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	if (isread)
		n = iswrite ? SHUT_RDWR : SHUT_RD;
	else if (iswrite)
		n = SHUT_WR;
	else
		return FALSE;

	if (KSKFN(shutdown)(self->fd, n) == 0)
		return TRUE;
	else
	{
		_k_sock_errno();
		return FALSE;
	}
}

/**
 * 소켓을 연결한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	nar			연결할 주소 정보.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_connect(kSocket* self, const kNar* nar)
{
	int ret, size;

	k_return_value_if_fail(nar != NULL, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);
	k_return_value_if_fail(!self->isconn, FALSE);

	if (k_nar_get_port(nar) == 0)
		return FALSE;

	size = k_nar_get_size(nar);

	if (size < 0)
		return FALSE;

#if _SB_WINDOWS_
	ret = KSKFN(WSAConnect)(self->fd, (struct sockaddr*)nar, size, NULL, NULL, NULL, NULL);
#else
	ret = KSKFN(connect)(self->fd, (struct sockaddr*)nar, size);
#endif

	if (ret == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}

	self->isconn = TRUE;

	return TRUE;
}

/**
 * 소켓으로 받을 수 있게 한다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	받지 못하면 널값을 받았으면 새로운 소켓을 반환한다.
 */
kSocket* k_sock_accept(kSocket* self)
{
	kSocket* res;
	kNar nar;
	socklen_t size;
	SOCKET fd;

	k_return_value_if_fail(self->islisten, NULL);

	size = (socklen_t)k_nar_get_size(&self->nar_bind);
	fd = KSKFN(accept)(self->fd, (struct sockaddr*)&nar, &size);

	if (fd == INVALID_SOCKET)
	{
		_k_sock_errno();
		return NULL;
	}

	res = k_sock_new_handle(fd, self->af, self->st, self->pt);

	if (!res)
	{
		KSKFN(shutdown)(fd, SHUT_RDWR);
		KSKFN(closesocket)(fd);
		return NULL;
	}

	res->isconn = TRUE;
	res->nar_bind = self->nar_bind;
	res->nar_rmt = nar;

	return res;
}

/**
 * 소켓을 받을 수 있게 바인드한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	nar			주소 정보.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_bind(kSocket* self, const kNar* nar)
{
	int size;

	k_return_value_if_fail(nar != NULL, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	size = k_nar_get_size(nar);

	if (size < 0)
		return FALSE;

	if (KSKFN(bind)(self->fd, (struct sockaddr*)nar, size) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}

	memcpy(&self->nar_bind, nar, sizeof(kNar));

	return TRUE;
}

/**
 * 소켓을 받을 수 있게 바인드 한다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	nai			주소 정보.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_bind_ai(kSocket* self, const kNai* nai)
{
	k_return_value_if_fail(nai, FALSE);

	return k_sock_bind(self, &nai->nar);
}

/**
 * 소켓을 받을 수 있게 최대 받는 개수를 지정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	maxconn 	최대 연결 개수로 0 또는 그 이하 값일 경우 최대 연결 개수(SOMAXCONN)로 지정한다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_listen(kSocket* self, int maxconn)
{
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	if (maxconn <= 0)
		maxconn = SOMAXCONN;

	if (KSKFN(listen)(self->fd, maxconn) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}

	self->islisten = TRUE;

	return TRUE;
}

#if _SB_WINDOWS_DESKTOP_ || _SB_WINDOWS_PHONE_ || _SB_APPLE_
static int _k_sock_poll_emul(SOCKET fd, int mode, int timeout)
{
	fd_set ffd[3], *pfd[3];
	struct timeval tv, *ptv;
	int ret;

	if (timeout < 0)
		ptv = NULL;
	else
	{
		if (timeout == 0)
		{
			tv.tv_sec = 0;
			tv.tv_usec = 0;
		}
		else
		{
			tv.tv_sec = timeout / 1000;
			tv.tv_usec = (timeout % 1000) * 1000;
		}

		ptv = &tv;
	}

	if ((mode & KPOLL_IN) == 0)
		pfd[0] = NULL;
	else
	{
		pfd[0] = &ffd[0];
		FD_ZERO(&ffd[0]);
		FD_SET(fd, pfd[0]);
	}

	if ((mode & KPOLL_OUT) == 0)
		pfd[1] = NULL;
	else
	{
		pfd[1] = &ffd[1];
		FD_ZERO(&ffd[1]);
		FD_SET(fd, pfd[1]);
	}

	pfd[2] = &ffd[2];
	FD_ZERO(&ffd[2]);
	FD_SET(fd, pfd[2]);

#if _SB_WINDOWS_
	ret = KSKFN(select)(0, pfd[0], pfd[1], pfd[2], ptv);
#else
	ret = KSKFN(select)(fd + 1, pfd[0], pfd[1], pfd[2], ptv);
#endif

	if (ret == 0)
		return 0;

	if (ret == SOCKET_ERROR)
	{
		_k_sock_errno();
		return -1;
	}

	ret = 0;

	if (pfd[0] && FD_ISSET(fd, pfd[0]))
		ret |= KPOLL_IN;

	if (pfd[1] && FD_ISSET(fd, pfd[1]))
		ret |= KPOLL_OUT;

	if (FD_ISSET(fd, pfd[2]))
		ret |= KPOLL_ERR;

	return ret;
}
#endif

static int _k_sock_poll_main(SOCKET fd, int mode, int timeout)
{
#if _SB_WINDOWS_
#if _SB_WINDOWS_PHONE_
	// 폰은 WSAPoll이 없음
	return _k_sock_poll_emul(fd, mode, timeout);
#else
#if _SB_WINDOWS_DESKTOP_
	if (!KSKFN(WSAPoll))
	{
		// 음... 8.1부터 된다는 것 같기도 하고..
		return _k_sock_poll_emul(fd, mode, timeout);
	}
	else
#endif
	{
		int ret;
		WSAPOLLFD pfd;

		pfd.fd = fd;
		pfd.events = 0;
		pfd.revents = 0;

		if (mode & KPOLL_IN)
			pfd.events |= POLLIN;

		if (mode & KPOLL_OUT)
			pfd.events |= POLLOUT;

		ret = KSKFN(WSAPoll)(&pfd, 1, timeout);

		if (ret == SOCKET_ERROR)
		{
			_k_sock_errno();
			return -1;
		}

		if (ret == 0)
			return 0;

		ret = 0;

		if (pfd.revents & POLLERR)
			ret |= KPOLL_ERR;

		if (pfd.revents & POLLHUP)
			ret |= KPOLL_HUP;

		if (pfd.revents & POLLNVAL)
			ret |= KPOLL_NVAL;

		if (pfd.revents & POLLPRI)
			ret |= KPOLL_PRI;

		if (pfd.revents & (POLLRDBAND | POLLRDNORM))
			ret |= KPOLL_IN;

		if (pfd.revents & POLLWRNORM)
			ret |= KPOLL_OUT;

		return ret;
	}
#endif	// _SB_WINDOWS_PHONE_
#elif _SB_APPLE_
	return _k_sock_poll_emul(fd, mode, timeout);
#else
	int ret;
	struct pollfd pfd;

	pfd.fd = fd;
	pfd.events = 0;
	pfd.revents = 0;

	if (mode & KPOLL_IN)
		pfd.events |= POLLIN;

	if (mode & KPOLL_OUT)
		pfd.events |= POLLOUT;

	ret = KSKFN(poll)(&pfd, 1, timeout);

	if (ret < 0)
	{
		_k_sock_errno();
		return -1;
	}

	if (ret == 0)
		return 0;

	ret = 0;

	if (pfd.revents & POLLERR)
		ret |= KPOLL_ERR;

	if (pfd.revents & POLLHUP)
		ret |= KPOLL_HUP;

	if (pfd.revents & POLLNVAL)
		ret |= KPOLL_NVAL;

	if (pfd.revents & POLLPRI)
		ret |= KPOLL_PRI;

	if (pfd.revents & POLLIN)
		ret |= KPOLL_IN;

	if (pfd.revents & POLLOUT)
		ret |= KPOLL_OUT;

	return ret;
#endif	// _SB_APPLE_
}

/**
 * 소켓을 대기(poll) 한다.
 * @param [입력]	self		개체나 인터페이스의 자기 자신 값.
 * @param	mode			대기 모드. KPOLL_ 정의를 참조.
 * @param	milliseconds	대기할 최대 시간을 지정하며 밀리초 단위.
 * @return	소켓 대기 상태를 얻는다. KPOLL_ 정의를 참조
 */
int k_sock_poll(kSocket* self, int mode, int milliseconds)
{
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	return mode == 0 ? 0 : _k_sock_poll_main(self->fd, mode, milliseconds);
}

/**
 * 소켓의 로컬 끝점을 얻는다. 로컬 기기의 연결 주소와 포트를 주소로 만들어 준다.
 * @param [입력]	self   	개체나 인터페이스의 자기 자신 값.
 * @param [반환]	nar	(널값이 아님) 로컬 끝점.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_get_local(kSocket* self, kNar* nar)
{
	k_return_value_if_fail(nar != NULL, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	if (!k_nar_test(&self->nar_local))
	{
		// socklen_t
		// - BSD, 윈도우에서는 signed
		// - 리눅스에서는 unsigned
		socklen_t size = (socklen_t)k_nar_get_size(&self->nar_local);

		if ((int)size < 0)
		{
			if (self->islisten)
				size = (socklen_t)k_nar_get_size(&self->nar_bind);
			else
				size = (socklen_t)_k_sock_get_family_size(self->af);
		}

		if (KSKFN(getsockname)(self->fd, (struct sockaddr*)&self->nar_local, &size) == SOCKET_ERROR)
		{
			_k_sock_errno();
			self->nar_local.family = 0;
			return FALSE;
		}
	}

	memcpy(nar, &self->nar_local, sizeof(kNar));

	return TRUE;
}

/**
 * 소켓 원격 끝점 즉, 상대방의 주소와 연결 포트를 주소로 얻을 수 있다.
 * @param [입력]	self   	개체나 인터페이스의 자기 자신 값.
 * @param [입력,반환]	nar	(널값이 아님) 원격 끝점.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_get_remote(kSocket* self, kNar* nar)
{
	k_return_value_if_fail(nar != NULL, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	if (!k_nar_test(&self->nar_rmt))
	{
		socklen_t size = (socklen_t)k_nar_get_size(&self->nar_rmt);

		if (KSKFN(getpeername)(self->fd, (struct sockaddr*)&self->nar_rmt, &size) == SOCKET_ERROR)
		{
			_k_sock_errno();
			self->nar_rmt.family = 0;
			return FALSE;
		}
	}

	memcpy(nar, &self->nar_rmt, sizeof(kNar));

	return TRUE;
}

/**
 * 소켓이 사용할 수 있는가 확인한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	검사에 성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_test(kSocket* self)
{
	return self->fd != INVALID_SOCKET;
}

/**
 * 소켓이 연결되어 있는지 확인한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_is_conn(kSocket* self)
{
	return self->isconn;
}

/**
 * 소켓이 블록킹 모드인지 확인한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_get_block(kSocket* self)
{
	return self->isblock;
}

/**
 * 소켓이 대기 모드인지 확인한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_is_listen(kSocket* self)
{
	return self->islisten;
}

/**
 * 소켓이 선택 모드인지 확인한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return 선택 모드.
 */
kint k_sock_get_select(kSocket* self)
{
	return self->selmode;
}

/**
 * 소켓의 패밀리를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	패밀리.
 */
kint k_sock_get_family(kSocket* self)
{
	return self->af;
}

/**
 * 소켓의 타입 형식을 얻는다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	타입 형식.
 */
kint k_sock_get_sock_type(kSocket* self)
{
	return self->st;
}

/**
 * 소켓의 프로토콜을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	프로토콜.
 */
kint k_sock_get_protocol(kSocket* self)
{
	return self->pt;
}

// 인라인 플래그
static kint _k_sock_rw_flags(int v)
{
	if (v == 0)
		return 0;
	else
	{
		int ret = 0;

		if (v & KNSF_OUTOFBAND)
			ret |= MSG_OOB;

		if (v & KNSF_PEEK)
			ret |= MSG_PEEK;

		if (v & KNSF_DONTROUTE)
			ret |= MSG_DONTROUTE;

		return ret;
	}
}

/**
 * 소켓을 통해 받는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	받을 데이터를 저장할 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		버퍼의 크기.
 * @param	flags   	받기 플래그. KNSF_ 를 참조한다.
 * @return	받은 크기를 반환한다.
 */
int k_sock_recv(kSocket* self, kpointer buffer, int offset, int size, int flags)
{
	char* ptr;
	int rf, ret;

	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(offset >= 0, -1);
	k_return_value_if_fail(size > 0, 0);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	rf = _k_sock_rw_flags(flags);
	k_return_value_if_fail(rf >= 0, -1);

	ptr = (char*)buffer;
	ret = KSKFN(recv)(self->fd, ptr + offset, size, rf);

	if (ret == SOCKET_ERROR)
		_k_sock_errno();

	return ret;
}

/**
 * 소켓을 통해 보낸다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	보낼 데이터 버퍼.
 * @param	offset  	보낼 데이터의 옵셋.
 * @param	size		보낼 데이터의 크기.
 * @param	flags   	보내기 플래그. KNSF_ 를 참조한다
 * @return	보낸 크기를 반환한다.
 */
int k_sock_send(kSocket* self, kconstpointer buffer, int offset, int size, int flags)
{
	const char* ptr;
	int rf, ret, tmp;

	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(offset >= 0, -1);
	k_return_value_if_fail(size > 0, 0);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	rf = _k_sock_rw_flags(flags);
	k_return_value_if_fail(rf >= 0, -1);

	ptr = (const char*)buffer;

	if ((flags & KNSF_FULLDATA) == 0)
	{
		ret = KSKFN(send)(self->fd, ptr + offset, size, rf);

		if (ret == SOCKET_ERROR)
			_k_sock_errno();
	}
	else
	{
		ret = 0;

		do
		{
			tmp = KSKFN(send)(self->fd, ptr + offset + ret, size - ret, rf);

			if (tmp == SOCKET_ERROR)
			{
				_k_sock_errno();
				return -1;
			}

			ret += tmp;
		} while (ret < size);
	}

	return ret;
}

/**
 * 소켓을 통해 받는데, 플래그를 사용하지 않는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	받을 데이터를 보관할 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		버퍼의 크기.
 * @return	받은 크기.
 */
kint k_sock_recv_nof(kSocket* self, kpointer buffer, kint offset, kint size)
{
	return k_sock_recv(self, buffer, offset, size, 0);
}

/**
 * 소켓을 통해 보내는데, 플래그를 사용하지 않는다. *
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	보낼 데이터 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		버퍼의 크기.
 * @return	보낸 크기.
 */
kint k_sock_send_nof(kSocket* self, kconstpointer buffer, kint offset, kint size)
{
	return k_sock_send(self, buffer, offset, size, 0);
}

/**
 * 소켓을 통해 스트림을 받는다.
 * @param [입력]	self   	개체나 인터페이스의 자기 자신 값.
 * @param	buffer	   	받을 데이터를 담을 버퍼.
 * @param	offset	   	버퍼의 옵셋.
 * @param	size	   	버퍼의 크기.
 * @param [입력,반환]	nar	(널값이 아님) 받는 주소 정보.
 * @param	flags	   	플래그. KNSF_ 를 참조한다.
 * @return	받은 크기.
 */
int k_sock_recv_from(kSocket* self, kpointer buffer, int offset, int size, kNar* nar, int flags)
{
	char* ptr;
	int rf, ret;
	socklen_t len;

	k_return_value_if_fail(nar != NULL, -1);
	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(offset >= 0, -1);
	k_return_value_if_fail(size > 0, 0);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	rf = _k_sock_rw_flags(flags);
	k_return_value_if_fail(rf >= 0, -1);

	len = (socklen_t)_k_sock_get_family_size(self->af);
	k_return_value_if_fail(len > 0, -1);

	ptr = (char*)buffer;
	ret = KSKFN(recvfrom)(self->fd, ptr + offset, size, rf, (struct sockaddr*)nar, &len);

	if (ret == SOCKET_ERROR)
		_k_sock_errno();

	return ret;
}

/**
 * 소켓을 통해 스트림을 보낸다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	보낼 데이터 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		버퍼의 크기.
 * @param	nar			보낼 주소 정보.
 * @param	flags	   	플래그. KNSF_ 를 참조한다.
 * @return	보낸 크기.
 */
int k_sock_send_to(kSocket* self, kconstpointer buffer, int offset, int size, const kNar* nar, int flags)
{
	const char* ptr;
	int rf, ret, tmp, len;

	k_return_value_if_fail(nar != NULL, -1);
	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(offset >= 0, -1);
	k_return_value_if_fail(size > 0, 0);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);
	k_return_value_if_fail(self->af == nar->family, -1);

	rf = _k_sock_rw_flags(flags);
	k_return_value_if_fail(rf >= 0, -1);

	len = k_nar_get_size(nar);
	k_return_value_if_fail(len > 0, -1);

	ptr = (const char*)buffer;

	if ((flags & KNSF_FULLDATA) == 0)
	{
		ret = KSKFN(sendto)(self->fd, ptr + offset, size, rf, (const struct sockaddr*)nar, len);

		if (ret == SOCKET_ERROR)
			_k_sock_errno();
	}
	else
	{
		ret = 0;

		do
		{
			tmp = KSKFN(sendto)(self->fd, ptr + offset + ret, size - ret, rf, (const struct sockaddr*)nar, len);

			if (tmp == SOCKET_ERROR)
			{
				_k_sock_errno();
				return -1;
			}

			ret += tmp;
		} while (ret < size);
	}

	return ret;
}

/**
 * 소켓의 블로킹 기능을 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_block(kSocket* self, kcham value)
{
#if _SB_WINDOWS_
	u_long n;
#else
	kint ret;
#endif

	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

#if _SB_WINDOWS_
	n = value ? 0 : 1;

	if (KSKFN(ioctlsocket)(self->fd, FIONBIO, &n) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}
#else
	int f = KSKFN(fcntl)(self->fd, F_GETFL);

	if (f == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}

	if (!value)
		ret = KSKFN(fcntl)(self->fd, F_SETFL, f | O_NONBLOCK);
	else
		ret = KSKFN(fcntl)(self->fd, F_SETFL, f & (~O_NONBLOCK));

	if (ret == SOCKET_ERROR)
		_k_sock_errno();
#endif

	self->isblock = value;

	return TRUE;
}

// 옵션 바인딩
static int _k_sock_bind_opt(int level, int name, int* syslevel, int* sysname)
{
	switch (level)
	{
		case KNSOL_SOCKET:
			*syslevel = SOL_SOCKET;

			switch (name)
			{
				case KNSON_DONTLINGER:
					*sysname = SO_LINGER;
					break;

				case KNSON_DEBUG:
					*sysname = SO_DEBUG;
					break;

				case KNSON_ACCEPTCONN:
					*sysname = SO_ACCEPTCONN;
					break;

				case KNSON_REUSEADDR:
					*sysname = SO_REUSEADDR;
					break;

				case KNSON_KEEPALIVE:
					*sysname = SO_KEEPALIVE;
					break;

				case KNSON_DONTROUTE:
					*sysname = SO_DONTROUTE;
					break;

				case KNSON_BROADCAST:
					*sysname = SO_BROADCAST;
					break;

				case KNSON_LINGER:
					*sysname = SO_LINGER;
					break;

				case KNSON_OUTOFBANDINLINE:
					*sysname = SO_OOBINLINE;
					break;

				case KNSON_SENDBUF:
					*sysname = SO_SNDBUF;
					break;

				case KNSON_RECVBUF:
					*sysname = SO_RCVBUF;
					break;

				case KNSON_SENDLOWWATER:
					*sysname = SO_SNDLOWAT;
					break;

				case KNSON_RECVLOWWATER:
					*sysname = SO_RCVLOWAT;
					break;

				case KNSON_SENDTIMEOUT:
					*sysname = SO_SNDTIMEO;
					break;

				case KNSON_RECVTIMEOUT:
					*sysname = SO_RCVTIMEO;
					break;

				case KNSON_ERROR:
					*sysname = SO_ERROR;
					break;

				case KNSON_TYPE:
					*sysname = SO_TYPE;
					break;

				case KNSON_EXCLUSIVEADDRUSE:
				case KNSON_USELOOPBACK:
				case KNSON_MAXCONN:
				default:
					k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is not supported socket name.", name);
					return -1;
			}

			break;

		case KNSOL_IP:
#ifdef SOL_IP
			*syslevel = SOL_IP;
#else

			if (!k_impl_sk.sol_ip_cache)
			{
				struct protoent* pent;
				pent = KSKFN(getprotobyname)("IP");
				k_impl_sk.sol_ip_proto = pent ? pent->p_proto : 0;
				k_impl_sk.sol_ip_cache = TRUE;
			}

			*syslevel = k_impl_sk.sol_ip_proto;
#endif

			switch (name)
			{
				case KNSON_IPOPTIONS:
					*sysname = IP_OPTIONS;
					break;

#ifdef IP_HDRINCL
				case KNSON_HEADERINCL:
					*sysname = IP_HDRINCL;
					break;
#endif

#ifdef IP_TOS
				case KNSON_TYPEOFSERVICE:
					*sysname = IP_TOS;
					break;
#endif

#ifdef IP_TTL
				case KNSON_IPTIMETOLIVE:
					*sysname = IP_TTL;
					break;
#endif

				case KNSON_MULTICASTINTERFACE:
					*sysname = IP_MULTICAST_IF;
					break;

				case KNSON_MULTICASTTIMETOLIVE:
					*sysname = IP_MULTICAST_TTL;
					break;

				case KNSON_MULTICASTLOOPBACK:
					*sysname = IP_MULTICAST_LOOP;
					break;

				case KNSON_ADDMEMBERSHIP:
					*sysname = IP_ADD_MEMBERSHIP;
					break;

				case KNSON_DROPMEMBERSHIP:
					*sysname = IP_DROP_MEMBERSHIP;
					break;

#ifdef IP_PKTINFO
				case KNSON_PACKETINFO:
					*sysname = IP_PKTINFO;
					break;
#endif

#ifdef IP_DONTFRAGMENT
				case KNSON_DONTFRAGMENT:
					*sysname = IP_DONTFRAGMENT;
					break;
#elif defined IP_MTU_DISCOVER
				case KNSON_DONTFRAGMENT:
					*sysname = IP_MTU_DISCOVER;
					break;
#else
				case KNSON_DONTFRAGMENT:
					return -2;
#endif

				case KNSON_ADDSRCMEMBERSHIP:
				case KNSON_DROPSRCMEMBERSHIP:
				case KNSON_BLOCKSRC:
				case KNSON_UNBLOCKSRC:
				default:
					k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is not supported ip name.", name);
					return -1;
			}

			break;

#ifdef AF_INET6
		case KNSOL_IPV6:
#ifdef SOL_IPV6
			*syslevel = SOL_IPV6;
#else

			if (!k_impl_sk.sol_ipv6_cache)
			{
				struct protoent* pent;
				pent = KSKFN(getprotobyname)("IPV6");
				k_impl_sk.sol_ipv6_proto = pent ? pent->p_proto : 41;
				k_impl_sk.sol_ipv6_cache = TRUE;
			}

			*syslevel = k_impl_sk.sol_ipv6_proto;
#endif

			switch (name)
			{
				case KNSON_IPTIMETOLIVE:
					*sysname = IPV6_UNICAST_HOPS;
					break;

				case KNSON_MULTICASTINTERFACE:
					*sysname = IPV6_MULTICAST_IF;
					break;

				case KNSON_MULTICASTTIMETOLIVE:
					*sysname = IPV6_MULTICAST_HOPS;
					break;

				case KNSON_MULTICASTLOOPBACK:
					*sysname = IPV6_MULTICAST_LOOP;
					break;

				case KNSON_ADDMEMBERSHIP:
					*sysname = IPV6_JOIN_GROUP;
					break;

				case KNSON_DROPMEMBERSHIP:
					*sysname = IPV6_LEAVE_GROUP;
					break;

#ifdef IPV6_PKTINFO
				case KNSON_PACKETINFO:
					*sysname = IPV6_PKTINFO;
					break;
#endif

				case KNSON_HEADERINCL:
				case KNSON_IPOPTIONS:
				case KNSON_TYPEOFSERVICE:
				case KNSON_DONTFRAGMENT:
				case KNSON_ADDSRCMEMBERSHIP:
				case KNSON_DROPSRCMEMBERSHIP:
				case KNSON_BLOCKSRC:
				case KNSON_UNBLOCKSRC:
				default:
					k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is not supported ipv6 name.", name);
					return -1;
			}

			break;
#endif

		case KNSOL_TCP:
#ifdef SOL_TCP
			*syslevel = SOL_TCP;
#else

			if (!k_impl_sk.sol_tcp_cache)
			{
				struct protoent* pent;
				pent = KSKFN(getprotobyname)("TCP");
				k_impl_sk.sol_tcp_proto = pent ? pent->p_proto : 6;
				k_impl_sk.sol_tcp_cache = TRUE;
			}

			*syslevel = k_impl_sk.sol_tcp_proto;
#endif

			switch (name)
			{
#ifdef TCP_NODELAY
				case KNSON_NODELAY:
					*sysname = TCP_NODELAY;
					break;
#endif

				default:
					k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is not supported tcp name.", name);
					return -1;
			}

			break;

		case KNSOL_UDP:
#ifdef SOL_UDP
			*syslevel = SOL_UDP;
#else

			if (!k_impl_sk.sol_udp_cache)
			{
				struct protoent* pent;
				pent = KSKFN(getprotobyname)("UDP");
				k_impl_sk.sol_udp_proto = pent ? pent->p_proto : 17;
				k_impl_sk.sol_udp_cache = TRUE;
			}

			*syslevel = k_impl_sk.sol_udp_proto;
#endif

			switch (name)
			{
				case KNSON_NOCHECKSUM:
				case KNSON_CHECKSUMCOVERAGE:
				default:
					k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is not supported udp name.", name);
					return -1;
			}

			break;

		default:
			k_tracef(KLV_WARNING, "k.sock.opt", "0x%x is unknown level.", level);
			return -1;
	}

	return 0;
}

/**
 * 소켓의 옵션을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	level   	옵션 레벨 KNSOL.
 * @param	name		옵션 이름 KNSON.
 * @param	data		옵션을 받을 데이터 버퍼.
 * @param	size		버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_get_opt(kSocket* self, int level, int name, kpointer data, int size)
{
	int ret, syslevel, sysname, nval;
#if _SB_WINDOWS_
	int tmp;
#else
	kuint tmp;
#endif
	struct linger lg;

	k_return_value_if_fail(data != NULL, FALSE);
	k_return_value_if_fail(size >= 0, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	ret = _k_sock_bind_opt(level, name, &syslevel, &sysname);
	k_return_value_if_fail(ret >= 0, FALSE);

	lg.l_onoff = 0;
	lg.l_linger = 0;
	nval = 0;

	switch (name)
	{
		case KNSON_LINGER:
		case KNSON_DONTLINGER:
			ret = KSKFN(getsockopt)(self->fd, syslevel, sysname, (char*)&lg, &tmp);
			break;

		default:
			tmp = sizeof(int);
			ret = KSKFN(getsockopt)(self->fd, syslevel, sysname, (char*)&nval, &tmp);
			break;
	}

	if (ret == SOCKET_ERROR)
	{
		_k_sock_errno();
		return FALSE;
	}

	switch (name)
	{
		case KNSON_LINGER:
			if (size >= (kint)(sizeof(int) * 2))
			{
				*(((int*)data) + 0) = lg.l_onoff;
				*(((int*)data) + 1) = lg.l_linger;
			}

			break;

		case KNSON_DONTLINGER:
			if (size >= (kint)sizeof(int))
				*(int*)data = lg.l_onoff;

			break;

		default:
			if (size >= (kint)sizeof(int))
				*(int*)data = nval;

			break;
	}

	return TRUE;
}

/**
 * 소켓의 옵션을 써 넣는다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	level   	옵션 레벨 KNSOL.
 * @param	name		옵션 이름 KNSON.
 * @param	data		옵션 데이터 버퍼.
 * @param	size		버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_opt(kSocket* self, int level, int name, kconstpointer data, int size)
{
	int ret, syslevel, sysname;
	struct linger lg;
#ifdef IP_PMTUDISC_DO
	int tmp;
#endif

	k_return_value_if_fail(data != NULL, FALSE);
	k_return_value_if_fail(size >= 0, FALSE);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, FALSE);

	ret = _k_sock_bind_opt(level, name, &syslevel, &sysname);
	k_return_value_if_fail(ret >= 0, FALSE);

	switch (name)
	{
		case KNSON_DONTLINGER:
			lg.l_onoff = 0;
			lg.l_linger = 0;
			ret = KSKFN(setsockopt)(self->fd, syslevel, sysname, (const char*)&lg, sizeof(struct linger));
			break;

		case KNSON_LINGER:
			if (size < (kint)(sizeof(int) * 2))
				ret = SOCKET_ERROR;
			else
			{
				lg.l_onoff = (u_short)* (((int*)data) + 0);
				lg.l_linger = (u_short)* (((int*)data) + 1);
				ret = KSKFN(setsockopt)(self->fd, syslevel, sysname, (const char*)&lg, sizeof(struct linger));
			}

			break;

		case KNSON_ADDMEMBERSHIP:
		case KNSON_DROPMEMBERSHIP:
			ret = SOCKET_ERROR;
			break;

#ifdef IP_PMTUDISC_DO
		case KNSON_DONTFRAGMENT:
			tmp = *(int*)data;

			if (tmp == 1)
				tmp = IP_PMTUDISC_DO;

			data = &tmp;
#endif

		default:
			ret = KSKFN(setsockopt)(self->fd, syslevel, sysname, (const char*)data, size);
			break;
	}

	if (ret != SOCKET_ERROR)
		return TRUE;
	else
	{
		_k_sock_errno();
		return FALSE;
	}
}

/**
 * 소켓 읽기 버퍼의 데이터 크기.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	읽을 수 있는 크기.
 */
int k_sock_get_avail(kSocket* self)
{
#if _SB_WINDOWS_
	u_long n;
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	if (KSKFN(ioctlsocket)(self->fd, FIONREAD, &n) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return -1;
	}

	return (int)n;
#elif _SB_APPLE_
	int n;
	socklen_t len = sizeof(int);
	k_return_value_if_fail(self->fd != INVALID_SOCKET, -1);

	if (KSKFN(getsockopt)(self->fd, SOL_SOCKET, SO_NREAD, &n, &len) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return -1;
	}

	return n;
#else
	int n;

	if (KSKFN(ioctl)(self->fd, FIONREAD, &n) == SOCKET_ERROR)
	{
		_k_sock_errno();
		return -1;
	}

	return n;
#endif
}

/**
 * 소켓의 프래그먼트 정보를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_dont_frag(kSocket* self, kcham value)
{
	switch (self->af)
	{
		case KNAF_INET:
			return k_sock_set_opt(self, KNSOL_IP, KNSON_DONTFRAGMENT, &value, sizeof(int));

		case KNAF_INET6:
			return k_sock_set_opt(self, KNSOL_IPV6, KNSON_DONTFRAGMENT, &value, sizeof(int));
	}

	return FALSE;
}

/**
 * 소켓의 프래그먼트 정보를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	사용 여부를 반환한다.
 */
kcham k_sock_get_dont_frag(kSocket* self)
{
	kcham ret, value;

	switch (self->af)
	{
		case KNAF_INET:
			ret = k_sock_get_opt(self, KNSOL_IP, KNSON_DONTFRAGMENT, &value, sizeof(int));
			break;

		case KNAF_INET6:
			ret = k_sock_get_opt(self, KNSOL_IPV6, KNSON_DONTFRAGMENT, &value, sizeof(int));
			break;

		default:
			ret = FALSE;
			value = FALSE;
			break;
	}

	return ret ? value : FALSE;
}

/**
 * 소켓의 브로드캐스트 사용 여부를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_broadcast(kSocket* self, kcham value)
{
	if (self->pt != KNPROT_UDP)
		return FALSE;

	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_BROADCAST, &value, sizeof(int));
}

/**
 * 소켓의 브로드캐스트 사용 여부를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	사용 여부를 반환한다.
 */
kcham k_sock_get_broadcast(kSocket* self)
{
	kcham value;

	if (self->pt != KNPROT_UDP)
		return FALSE;

	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_BROADCAST, &value, sizeof(int)) ? value : FALSE;
}

/**
 * 프로세스 당 소켓 사용 여부를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_exaddr_use(kSocket* self, kcham value)
{
	if (self->islisten)
		return FALSE;

	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_EXCLUSIVEADDRUSE, &value, sizeof(int));
}

/**
 * 프로세스 당 소켓 사용 여부를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	사용 여부를 반환한다.
 */
kcham k_sock_get_exaddr_use(kSocket* self)
{
	kcham value;
	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_EXCLUSIVEADDRUSE, &value, sizeof(int)) ? value : FALSE;
}

/**
 * 소켓 링거 기능 사용 여부를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	onoff   	켬끔 정보.
 * @param	linger  	링거 정보.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_linger(kSocket* self, int onoff, int linger)
{
	int lg[2];
	lg[0] = onoff;
	lg[1] = linger;
	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_LINGER, lg, sizeof(int) * 2);
}

/**
 * 소켓 링거 기능 사용 여부를 얻는다.
 * @param [입력]	self	  	개체나 인터페이스의 자기 자신 값.
 * @param [반환]	onoff 	(널값이 아님) 켬끔 정보.
 * @param [반환]	linger	(널값이 아님) 링거 정보.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_get_linger(kSocket* self, int* onoff, int* linger)
{
	int lg[2];

	if (!k_sock_get_opt(self, KNSOL_SOCKET, KNSON_LINGER, lg, sizeof(int) * 2))
		return FALSE;
	else
	{
		if (onoff) *onoff = lg[0];

		if (linger) * linger = lg[1];

		return TRUE;
	}
}

/**
 * 소켓의 멀티캐스트 루프백 사용 여부를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_mclb(kSocket* self, kcham value)
{
	if (self->pt == KNPROT_TCP)
		return FALSE;

	switch (self->af)
	{
		case KNAF_INET:
			return k_sock_set_opt(self, KNSOL_IP, KNSON_MULTICASTLOOPBACK, &value, sizeof(int));

		case KNAF_INET6:
			return k_sock_set_opt(self, KNSOL_IPV6, KNSON_MULTICASTLOOPBACK, &value, sizeof(int));
	}

	return FALSE;
}

/**
 * 소켓의 멀티캐스트 루프백 사용 여부를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	사용 여부를 얻는다.
 */
kcham k_sock_get_mclb(kSocket* self)
{
	kcham ret, value;

	if (self->pt == KNPROT_TCP)
		return FALSE;

	switch (self->af)
	{
		case KNAF_INET:
			ret = k_sock_get_opt(self, KNSOL_IP, KNSON_MULTICASTLOOPBACK, &value, sizeof(int));
			break;

		case KNAF_INET6:
			ret = k_sock_get_opt(self, KNSOL_IPV6, KNSON_MULTICASTLOOPBACK, &value, sizeof(int));
			break;

		default:
			ret = FALSE;
			value = FALSE;
			break;
	}

	return ret ? value : FALSE;
}

/**
 * 소켓의 받는 버퍼의 크기를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	size		받기 버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_recv_buf_size(kSocket* self, int size)
{
	k_return_value_if_fail(size > 0, FALSE);
	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_RECVBUF, &size, sizeof(int));
}

/**
 * 소켓의 받는 버퍼의 크기를 얻는다
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	버퍼 크기.
 */
int k_sock_get_recv_buf_size(kSocket* self)
{
	int value;
	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_RECVBUF, &value, sizeof(int)) ? value : 0;
}

/**
 * 소켓의 보내는 버퍼의 크기를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	size		보내기 버퍼의 크기.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_send_buf_size(kSocket* self, int size)
{
	k_return_value_if_fail(size > 0, FALSE);
	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_SENDBUF, &size, sizeof(int));
}

/**
 * 소켓의 보내는 버퍼의 크기를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	버퍼 크기.
 */
int k_sock_get_send_buf_size(kSocket* self)
{
	int value;
	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_SENDBUF, &value, sizeof(int)) ? value : 0;
}

/**
 * 소켓의 ttl 값을 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	ttl 값.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_ttl(kSocket* self, kshort value)
{
	switch (self->af)
	{
		case KNAF_INET:
			return k_sock_set_opt(self, KNSOL_IP, KNSON_IPTIMETOLIVE, &value, sizeof(kshort));

		case KNAF_INET6:
			return k_sock_set_opt(self, KNSOL_IPV6, KNSON_IPTIMETOLIVE, &value, sizeof(kshort));
	}

	return FALSE;
}

/**
 * 소켓의 ttl 값을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	ttl 값.
 */
kshort k_sock_get_ttl(kSocket* self)
{
	kcham ret;
	kshort value;

	switch (self->af)
	{
		case KNAF_INET:
			ret = k_sock_get_opt(self, KNSOL_IP, KNSON_IPTIMETOLIVE, &value, sizeof(kshort));
			break;

		case KNAF_INET6:
			ret = k_sock_get_opt(self, KNSOL_IPV6, KNSON_IPTIMETOLIVE, &value, sizeof(kshort));
			break;

		default:
			ret = FALSE;
			value = FALSE;
			break;
	}

	return ret ? value : 0;
}

/**
 * 소켓의 보내기 타임 아웃을 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	len			타임아웃(밀리초).
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_send_timeout(kSocket* self, int len)
{
	k_return_value_if_fail(len >= 0, FALSE);
	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_SENDTIMEOUT, &len, sizeof(int));
}

/**
 * 소켓의 보내기 타임 아웃을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	타임아웃(밀리초).
 */
int k_sock_get_send_timeout(kSocket* self)
{
	int len;
	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_SENDTIMEOUT, &len, sizeof(int)) ? len : -1;
}

/**
 * 소켓의 받기 타임 아웃을 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	len			타임아웃(밀리초).
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_recv_timeout(kSocket* self, int len)
{
	k_return_value_if_fail(len >= 0, FALSE);
	return k_sock_set_opt(self, KNSOL_SOCKET, KNSON_RECVTIMEOUT, &len, sizeof(int));
}

/**
 * 소켓의 받기 타임 아웃을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	타임아웃(밀리초).
 */
int k_sock_get_recv_timeout(kSocket* self)
{
	int len;
	return k_sock_get_opt(self, KNSOL_SOCKET, KNSON_RECVTIMEOUT, &len, sizeof(int)) ? len : -1;
}

/**
 * 소켓의 딜레이 여부를 설정한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	value   	기능을 쓰려면 참으로 넣는다.
 * @return	성공하면 참을, 실패하면 거짓을 반환한다.
 */
kcham k_sock_set_nodelay(kSocket* self, kcham value)
{
	if (self->pt == KNPROT_UDP)
		return FALSE;

	return k_sock_set_opt(self, KNSOL_TCP, KNSON_NODELAY, &value, sizeof(int));
}

/**
 * 소켓의 딜레이 여부를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	상태 값을 반환한다.
 */
kcham k_sock_get_nodelay(kSocket* self)
{
	kcham value;

	if (self->pt == KNPROT_UDP)
		return FALSE;

	return k_sock_get_opt(self, KNSOL_TCP, KNSON_NODELAY, &value, sizeof(int)) ? value : FALSE;
}

/**
 * 여러 소켓의 선택 모드를 처리한다.
 * @param [입력]	socks	(널값이 아님) 소켓 배열.
 * @param	count		 	배열의 개수
 * @param	mode		 	선택 모드. KPOLL_ 참조.
 * @param	milliseconds 	대기할 타임아웃(밀리초).
 * @return	선택 모드에서 이벤트가 발생한 소켓들의 개수.
 */
int k_sock_select(kSocket** socks, int count, int mode, int milliseconds)
{
	SOCKET fds[64];
	fd_set fd[3], *pfd[3];
	struct timeval tv, *ptv;
	int i, ret;
	SOCKET maxfd = 0;

	k_return_value_if_fail(socks != NULL, -1);
	k_return_value_if_fail(count > 0 && count <= 64, -1);
	k_return_value_if_fail(mode != 0, 0);

	for (i = 0; i < count; i++)
	{
		if (socks[i] == NULL)
			return -1;

		socks[i]->selmode = 0;

		fds[i] = socks[i]->fd;

		if (maxfd < fds[i])
			maxfd = fds[i];
	}

	if (maxfd == 0)
		return 0;

	if (milliseconds < 0)
		ptv = NULL;
	else
	{
		if (milliseconds == 0)
		{
			tv.tv_sec = 0;
			tv.tv_usec = 0;
		}
		else
		{
			tv.tv_sec = milliseconds / 1000;
			tv.tv_usec = (milliseconds % 1000) * 1000;
		}

		ptv = &tv;
	}

	if ((mode & KPOLL_IN) == 0)
		pfd[0] = NULL;
	else
	{
		pfd[0] = &fd[0];
		FD_ZERO(&fd[0]);

		for (i = 0; i < count; i++)
			FD_SET(fds[i], pfd[0]);
	}

	if ((mode & KPOLL_OUT) == 0)
		pfd[1] = NULL;
	else
	{
		pfd[1] = &fd[1];
		FD_ZERO(&fd[1]);

		for (i = 0; i < count; i++)
			FD_SET(fds[i], pfd[1]);
	}

	pfd[2] = &fd[2];
	FD_ZERO(&fd[2]);

	for (i = 0; i < count; i++)
		FD_SET(fds[i], pfd[2]);

#if _SB_WINDOWS_
	ret = KSKFN(select)(0, pfd[0], pfd[1], pfd[2], ptv);
#else
	ret = KSKFN(select)(maxfd + 1, pfd[0], pfd[1], pfd[2], ptv);
#endif

	if (ret == 0)
		return 0;

	if (ret == SOCKET_ERROR)
	{
		_k_sock_errno();
		return -1;
	}

	if (pfd[0])
	{
		for (i = 0; i < count; i++)
		{
			if (FD_ISSET(fds[i], pfd[0]))
				socks[i]->selmode |= KPOLL_IN;
		}
	}

	if (pfd[1])
	{
		for (i = 0; i < count; i++)
		{
			if (FD_ISSET(fds[i], pfd[1]))
				socks[i]->selmode |= KPOLL_OUT;
		}
	}

	for (i = 0; i < count; i++)
	{
		if (FD_ISSET(fds[i], pfd[2]))
			socks[i]->selmode |= KPOLL_ERR;
	}

	return ret;
}

/**
 * 소켓에 사용자 데이터를 입력한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	data		사용자 데이터.
 */
void k_sock_set_userdata(kSocket* self, kpointer data)
{
	self->userdata = data;
}

/**
 * 소켓에서 사용자 데이터를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	사용자 데이터 포인터.
 */
kpointer k_sock_get_userdata(kSocket* self)
{
	return self->userdata;
}

#undef KSKFN
